/*
 * Copyright (c) 2012 Aleksander Zotov, Benedikt Markt, Aliaksei Babkovich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 *  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 *  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 *  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import database.AGILogin;
import database.SourceDB;
import database.SpaceTrackLogin;

/**
 * This class represents preferences that affect the whole application
 * 
 * @author Benedikt Markt
 */
public class ApplicationPreferences {

	private static ApplicationPreferences instance = new ApplicationPreferences();

	private static final String CFG_FILE_PATH = "satpro.cfg";

	/**
	 * Defines which messages are printed to stdout
	 * 0 = TRACE helps to track down errors (for devs)
	 * 1 = DEBUG general debugging info (for admins etc)
	 * 2 = INFO useful information
	 * 3 = WARN potentially bad occurences
	 * 4 = ERROR errors that are fatal to a specifoc operation, but not the whole application
	 * 5 = FATAL errors that force a shutdown of the application
	 */
	private int logLevel = 2;
	/**
	 * url of the sql db
	 */
	private String dbURL = null;
	/**
	 * name of the sql db
	 */
	private String dbName = null;
	/**
	 * username to for sql db
	 */
	private String dbUsername = null;
	/**
	 * password for sql db
	 */
	private String dbPass = null;
	/**
	 * the maximum allowed concurrently running simulations
	 */
	private int maxConcurrentSims = 4;
	private ArrayList<SourceDB> sourceDBs = new ArrayList<SourceDB>();

	private ApplicationPreferences() {
		if (!loadPreferences()) {
			maxConcurrentSims = 2;
			Debug.log(
					Debug.WARN,
					"Config file not found or not readable. Creating new config file!",
					true);
			sourceDBs.add(new SpaceTrackLogin(null, null));
			sourceDBs.add(new AGILogin(null, null));
			savePreferences();
		} else {
			Debug.log(Debug.INFO, "Config file found. Loading preferences!",
					true);
		}
	}

	public static ApplicationPreferences getInstance() {
		return instance;
	}

	public int getLogLevel() {
		return logLevel;
	}

	public String getDbURL() {
		return dbURL;
	}

	public String getDbName() {
		return dbName;
	}

	public String getDbUsername() {
		return dbUsername;
	}

	public String getDbPass() {
		return dbPass;
	}

	public int getMaxConcurrentSims() {
		return maxConcurrentSims;
	}

	public ArrayList<SourceDB> getSourceDBs() {
		return sourceDBs;
	}

	public void setDatabase(String dbURL, String dbName, String dbUsername,
			String dbPass) {
		this.dbURL = dbURL;
		this.dbName = dbName;
		this.dbUsername = dbUsername;
		this.dbPass = dbPass;
		savePreferences();
	}

	public void addSourceDB(String username, String password, int type) {
		if (type == 0) {
			sourceDBs.remove(0);
			sourceDBs.add(0, new SpaceTrackLogin(username, password));
			savePreferences();
		}
		 if(type == 1) {
		 sourceDBs.remove(1);
		 sourceDBs.add(1, new SpaceTrackLogin(username, password));
		 savePreferences();
		 }
	}

	public void deleteSourceDB(int index) {
		sourceDBs.remove(index);
		savePreferences();
	}

	public void setMaxConcurrentSims(int maxConcurrentSims) {
		this.maxConcurrentSims = maxConcurrentSims;
		savePreferences();
	}

	/**
	 * Stores the preferences to a file
	 */
	private void savePreferences() {
		try {
			FileWriter prefsFile = new FileWriter(CFG_FILE_PATH);
			BufferedWriter out = new BufferedWriter(prefsFile);
			out.write("# _____  ___ _____");
			out.newLine();
			out.write("#/  ___|/ _ \\_   _|");
			out.newLine();
			out.write("#\\ `--./ /_\\ \\| |_ __  _ __ ___");
			out.newLine();
			out.write("# `--. \\  _  || | '_ \\| '__/ _ \\");
			out.newLine();
			out.write("#/\\__/ / | | || | |_) | | | (_) |");
			out.newLine();
			out.write("#\\____/\\_| |_/\\_/ .__/|_|  \\___/");
			out.newLine();
			out.write("#               | |");
			out.newLine();
			out.write("#               |_|");
			out.newLine();
			out.write("#SatPro config file. Only edit if you know what you're doing!");
			out.newLine();
			out.write("# Defines which messages are printed to stdout");
			out.newLine();
			out.write("# 0 = TRACE helps to track down errors (for devs)");
			out.newLine();
			out.write("# 1 = DEBUG general debugging info (for admins etc)");
			out.newLine();
			out.write("# 2 = INFO useful information");
			out.newLine();
			out.write("# 3 = WARN potentially bad occurences");
			out.newLine();
			out.write("# 4 = ERROR errors that are fatal to a specifoc operation, but not the whole application");
			out.newLine();
			out.write("# 5 = FATAL errors that force a shutdown of the application");
			out.newLine();
			out.write("Log Level = 1");
			out.newLine();
			out.write("Database URL = " + dbURL);
			out.newLine();
			out.write("Database Name = " + dbName);
			out.newLine();
			out.write("Database Username = " + dbUsername);
			out.newLine();
			out.write("Database Password = " + dbPass);
			out.newLine();
			out.write("Max Concurrent Simulations = " + maxConcurrentSims);
			out.newLine();
			out.write("#Source DBs: \n#############################################");
			out.newLine();
			for (int i = 0; i < sourceDBs.size(); i++) {
				out.write("Source Database Name = "
						+ sourceDBs.get(i).getName());
				out.newLine();
				out.write("URL = " + sourceDBs.get(i).getUrl());
				out.newLine();
				out.write("Username = " + sourceDBs.get(i).getUsername());
				out.newLine();
				out.write("Password = " + sourceDBs.get(i).getPassword());
				out.newLine();
				out.write("#############################################");
				out.newLine();
			}

			out.close();
		} catch (Exception e) {
			// e.printStackTrace();
			System.out.println("Unable to write satpro.cfg!");
		}
	}

	/**
	 * Loads the preferences from a file
	 * 
	 */
	private boolean loadPreferences() {

		List<String> inText = new ArrayList<String>();
		sourceDBs.clear();

		try {
			FileInputStream prefsFile = new FileInputStream(CFG_FILE_PATH);
			DataInputStream in = new DataInputStream(prefsFile);

			BufferedReader reader = new BufferedReader(
					new InputStreamReader(in));
			String inLine = null;

			while ((inLine = reader.readLine()) != null) {
				inText.add(inLine);
			}

			in.close();

		} catch (Exception e) {
			// e.printStackTrace();
			return false;
		}

		for (int i = 0; i < inText.size(); i++) {
			if (inText.get(i).charAt(0) != '#') {
				String currentLine = inText.get(i);
				int cutMarker = currentLine.indexOf('=');
				String key = currentLine.substring(0, cutMarker).trim();
				String value = currentLine.substring(cutMarker + 1,
						currentLine.length()).trim();
				if (key.equals("Log Level")) {
					try {
						if (Integer.parseInt(value) >= 0
								&& Integer.parseInt(value) <= 5) {
							logLevel = Integer.parseInt(value);
						} else {
							return false;
						}
					} catch (NumberFormatException e) {
						return false;
					}
				} else if (key.equals("Database URL")) {
					dbURL = value;
				} else if (key.equals("Database Name")) {
					dbName = value;
				} else if (key.equals("Database Username")) {
					dbUsername = value;
				} else if (key.equals("Database Password")) {
					dbPass = value;
				} else if (key.equals("Max Concurrent Simulations")) {
					try {
						if (Integer.parseInt(value) > 0
								&& Integer.parseInt(value) < 100) {
							maxConcurrentSims = Integer.parseInt(value);
						} else {
							return false;
						}
					} catch (NumberFormatException e) {
						return false;
					}
				} else if (key.equals("Source Database Name")) {

					SourceDB temp;

					if (value.equals("SpaceTrack")) {
						temp = new SpaceTrackLogin();
					} else if (value.equals("AGI")) {
						temp = new AGILogin();
					} else {
						return false;
					}
					i++;

					for (int j = 0; j < 2; j++) {
						i++;
						if (i > inText.size() - 1) {
							return false;
						}
						if (inText.get(i).charAt(0) != '#') {
							currentLine = inText.get(i);
							cutMarker = currentLine.indexOf('=');
							key = currentLine.substring(0, cutMarker).trim();
							value = currentLine.substring(cutMarker + 1,
									currentLine.length()).trim();

							if (key.equals("Username")) {
								temp.setUsername(value);
							} else if (key.equals("Password")) {
								temp.setPassword(value);
							} else {
								return false;
							}
						} else {
							j--;
						}
					}

					sourceDBs.add(temp);

				}
			}
		}

		return true;

	}
}
